using IQIGame.Onigao.Framework;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.Game
{
    public partial class EntityManager : FrameMonoModule
    {
        /// <summary>
        /// 内部EntityId生成器
        /// </summary>
        private static int s_SerialEntityID;
        /// <summary>
        /// Entity组缓存
        /// </summary>
        private Dictionary<int, EntityGroup> m_EntityGroups { get; set; }

        /// <summary>
        /// 组件上EntityGroup的配置
        /// </summary>
        [SerializeField]
        private EntityGroupInfo[] m_EntityGroupInfos = null;

        protected override void OnInit()
        {
            FrameworkEntry.MonoDriver.updateEvent += OnUpdate;
        }

        protected override void OnEnterGame()
        {
            m_EntityGroups = new Dictionary<int, EntityGroup>();
            for (int i = 0; i < m_EntityGroupInfos.Length; i++)
            {
                EntityGroupInfo info = m_EntityGroupInfos[i];
                if (!info.DoNotAddOnInit)
                {
                    AddEntityGroup(info.Id, info.EntityType, info.InstanceAutoReleaseInterval, info.InstanceExpireTime, info.SameResInstanceCapacity, info.TotalInstanceCapacity);
                }
            }
        }

        /// <summary>
        /// 生成实体ID
        /// </summary>
        /// <param name="entityComponent"></param>
        /// <returns></returns>
        public static int GenerateEntityID()
        {
            return ++s_SerialEntityID;
        }

        private void OnUpdate(float deltaTime, float unScaledDeltaTime)
        {
            foreach (var kv in m_EntityGroups)
            {
                kv.Value.OnUpdate(deltaTime, unScaledDeltaTime);
            }
        }

        /// <summary>
        /// 增加实体组。
        /// </summary>
        /// <param name="groupId">实体组名称。</param>
        /// <param name="entityType">组内实体对象的类型</param>
        /// <param name="instanceAutoReleaseInterval">实体实例对象池自动释放可释放对象的间隔秒数。</param>
        /// <param name="instanceExpireTime">对象池实例过期秒数</param>
        /// <param name="sameResCapacity">同预设资源Entity容量</param>
        /// <param name="totalCapacity">所有Entity容量</param>
        /// <returns>是否增加实体组成功。</returns>
        private bool AddEntityGroup(int groupId, Type entityType, float instanceAutoReleaseInterval, float instanceExpireTime, int sameResCapacity, int totalCapacity)
        {
            if (groupId <= 0)
            {
                throw new FrameworkException($"Entity group name is invalid. [{groupId}]");
            }

            if (HasEntityGroup(groupId))
            {
                return false;
            }

            m_EntityGroups.Add(groupId, new EntityGroup(groupId, entityType, instanceAutoReleaseInterval, instanceExpireTime, sameResCapacity, totalCapacity));

            return true;
        }

        /// <summary>
        /// 激活已存在的实体组。
        /// </summary>
        /// <param name="groupId"></param>
        /// <returns></returns>
        public bool EnableGroup(int groupId)
        {
            for (int i = 0; i < m_EntityGroupInfos.Length; i++)
            {
                EntityGroupInfo info = m_EntityGroupInfos[i];
                if (info.Id == groupId)
                {
                    return AddEntityGroup(info.Id, info.EntityType, info.InstanceAutoReleaseInterval, info.InstanceExpireTime, info.SameResInstanceCapacity, info.TotalInstanceCapacity);
                }
            }
            return false;
        }

        /// <summary>
        /// 使已存在的实体组失效，同时释放内部所有实体。
        /// </summary>
        /// <param name="groupId"></param>
        /// <returns></returns>
        public bool DisableGroup(int groupId)
        {
            EntityGroup group = GetEntityGroup(groupId);
            if (group == null)
                return false;
            group.Dispose();
            m_EntityGroups.Remove(groupId);
            return true;
        }

        /// <summary>
        /// 是否存在实体组。
        /// </summary>
        /// <param name="groupId">实体组名称。</param>
        /// <returns>是否存在实体组。</returns>
        public bool HasEntityGroup(int groupId)
        {
            if (groupId <= 0)
            {
                throw new FrameworkException($"Entity group name is invalid. [{groupId}]");
            }

            return m_EntityGroups.ContainsKey(groupId);
        }

        /// <summary>
        /// 显示实体。
        /// </summary>
        /// <param name="entityId">实体编号。</param>
        /// <param name="entityAssetName">实体资源名称。</param>
        /// <param name="groupId">实体组名称。</param>
        /// <param name="priority">加载实体资源的优先级。</param>
        public Entity Get(string entityAssetName, int groupId)
        {
            if (string.IsNullOrEmpty(entityAssetName))
            {
                throw new FrameworkException("Entity asset name is invalid.");
            }

            if (groupId <= 0)
            {
                throw new FrameworkException($"Entity group is invalid. [{groupId}]");
            }

            EntityGroup entityGroup = GetEntityGroup(groupId);
            if (entityGroup == null)
            {
                throw new FrameworkException($"Entity group '{groupId}' is not exist.");
            }

            Entity entity = entityGroup.Get(GenerateEntityID(), entityAssetName);
            return entity;
        }

        public Entity Get(string entityAssetName, EntityGroup entityGroup)
        {
            Entity entity = entityGroup.Get(GenerateEntityID(), entityAssetName);
            return entity;
        }

        public void HideEntity(Entity entity)
        {
            if (entity == null)
            {
                throw new FrameworkException("Entity is null.");
            }
            entity.EntityGroup.HideEntity(entity);
        }

        public void HideAll()
        {
            foreach (var kv in m_EntityGroups)
            {
                kv.Value.HideAll();
            }
        }

        public EntityGroup GetEntityGroup(int groupId)
        {
            if (m_EntityGroups.TryGetValue(groupId, out var entityGroup))
            {
                return entityGroup;
            }
            return null;
        }

        protected override void OnShutdown()
        {
            FrameworkEntry.MonoDriver.updateEvent -= OnUpdate;
            foreach (var kv in m_EntityGroups)
            {
                kv.Value.Dispose();
            }
            m_EntityGroups.Clear();
        }
    }
}
